#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <sys/epoll.h>




#include "proxytest.h"




/*
 * +++++++++++++++++++
 * + request sample  +
 * +++++++++++++++++++
 *
 * HTTP:
 * GET http://www.google.com/ HTTP/1.1
 * User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
 * Host: www.google.com
 *
 * HTTPS:
 * CONNECT www.google.com:443 HTTP/1.1
 * User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
 * Host: www.google.com
 *
 */


/*
 * global shared data begin
 *
 */

static const char* http_target_host="www.timectrl.net";
static const char* http_target_port="80";


static const char* http_request="GET http://%s:%s/ HTTP/1.1\r\nUser-Agent: Mozilla/5.0 Firefox/9.0.1\r\nHost: %s\r\n\r\n";
static const char* https_request="CONNECT %s:%s HTTP/1.1\r\nUser-Agent: Mozilla/5.0 Firefox/9.0.1\r\nHost: %s\r\n\r\n";

/*
 * global shared data end
 *
 */






inline int time_taken(const proxy_result_info_t *result, time_sequence_type_t sequence)
{
	if( (sequence<=tst_begin)
			|| (sequence>=tst_end)
			|| (result->tv[sequence].tv_sec==0) )
	{
		return -1;
	}

	return 	(
			(
			 result->tv[sequence].tv_sec
			 -
			 result->tv[sequence-1].tv_sec
			)*1000
			+
			(
			 result->tv[sequence].tv_usec
			 -
			 result->tv[sequence-1].tv_usec
			)/1000
		);
}




int http_proxy_test(proxy_info_t *proxy_info)
{
	/*
	 * thread init begin
	 *   this will do only once for the thread
	 */

	int return_code=0;

	int success=-1;
	struct addrinfo hints;
	struct addrinfo *res=NULL;
	int sfd=-1;

	char *response=NULL;
	char *request=NULL;
	int request_len=0;

	/*
	fd_set fdset;
	struct timeval tv;
	int retval;
	*/

	int epollfd;
	int nfds;
	const int max_events=10;
	struct epoll_event ev, events[max_events];

	bzero(&proxy_info->result,sizeof(proxy_result_info_t));




	if(request==NULL)
	{
		request=malloc(128);
		sprintf( request,http_request, http_target_host, http_target_port, http_target_host);
		//sprintf( request,https_request, http_target_host, http_target_port, http_target_host);
		request_len=strlen(request);
	}
	if(response==NULL)
	{
		response=malloc(128);
	}
	/*
	 * thread init finished
	 */


	if(sfd==-1)
	{
		sfd=socket(AF_INET,SOCK_STREAM,0);
		if(sfd<0)
		{
			perror("socket");
			return_code=-1;
			goto EXIT;
		}
		fcntl(sfd,F_SETFL,O_NONBLOCK|fcntl(sfd,F_GETFL,0));
	}

	//proxy test loop init, this should be init in every loop
	bzero(&hints,sizeof(struct addrinfo));
	hints.ai_family = AF_INET;    /* Allow IPv4 or IPv6 */
	hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
	success=-1;

	epollfd=epoll_create(4096);
	if (epollfd == -1)
	{
		perror("epoll_create");
		return_code=-1;
		goto EXIT;
	}



	gettimeofday(&proxy_info->result.tv[tst_begin], NULL); //record start time
	success=getaddrinfo(proxy_info->request.host, proxy_info->request.port, &hints, &res);
	if(res==NULL||success!=0)
	{
		perror("getaddrinfo");
		return_code=-1;
		goto EXIT;
	}
	gettimeofday(&proxy_info->result.tv[tst_dns], NULL);


	success=connect(sfd, res->ai_addr, res->ai_addrlen);
	ev.events = EPOLLOUT;
	ev.data.fd = sfd;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) == -1)
	{
		perror("epoll_ctl: write");
		return_code=-1;
		goto EXIT;
	}
	nfds = epoll_wait(epollfd, events, max_events, 5*1000);
	if (nfds < 0)
	{
		perror("epoll_pwait connect");
		return_code=-1;
		goto EXIT;
	}
	else if(nfds == 0)
	{
		//timeout
		return_code=-1;
		goto EXIT;
	}
	freeaddrinfo(res);
	gettimeofday(&proxy_info->result.tv[tst_connect], NULL);






	write(sfd,request,request_len);
	ev.events = EPOLLOUT;
	ev.data.fd = sfd;
	if (epoll_ctl(epollfd, EPOLL_CTL_MOD, sfd, &ev) == -1)
	{
		perror("epoll_ctl: write");
		return_code=-1;
		goto EXIT;
	}
	nfds = epoll_wait(epollfd, events, max_events, 5*1000);
	if (nfds < 0)
	{
		perror("epoll_pwait");
		return_code=-1;
		goto EXIT;
	}
	else if(nfds == 0)
	{
		//timeout
		return_code=-1;
		goto EXIT;
	}
	gettimeofday(&proxy_info->result.tv[tst_request], NULL);




	ev.events = EPOLLIN;
	ev.data.fd = sfd;
	if (epoll_ctl(epollfd, EPOLL_CTL_MOD, sfd, &ev) == -1)
	{
		perror("epoll_ctl: listen_sock");
		return_code=-1;
		goto EXIT;
	}
	nfds = epoll_wait(epollfd, events, max_events, 20*1000);
	if (nfds < 0)
	{
		perror("epoll_pwait");
		return_code=-1;
		goto EXIT;
	}
	else if(nfds == 0)
	{
		//timeout
		return_code=-1;
		goto EXIT;
	}
	bzero(response,128);
	read(sfd, response,120);
	shutdown(sfd,SHUT_RDWR);
	gettimeofday(&proxy_info->result.tv[tst_response], NULL);

	if(strstr(response,"200"))
	{
		proxy_info->result.status.http=1;
		return_code=0;
		goto EXIT;
	}
	else
	{
		return_code=-1;
		goto EXIT;
	}



	/*
	 * thread exit 
	 */
	/*
	 * global resource, don't free
	 * if(request!=NULL)
	 * {
	 * 	free(request);
	 * }
	*/

EXIT:
	if(response!=NULL)
	{
		free(response);
	}
	if(request!=NULL)
	{
		free(request);
	}
	if(sfd>=0)
	{
		close(sfd);
		sfd=-1;
	}

	return return_code;

}


